package com.blackhorse.controller;

import com.alibaba.fastjson.JSON;
import com.blackhorse.domain.Order;
import com.blackhorse.domain.Product;
import com.blackhorse.service.OrderService;
import com.blackhorse.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * 测试nacos、ribbon、feign的controller
 * 使用时打开RestController注解
 */
@Slf4j
//@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private OrderService orderService;
    @Autowired
    private DiscoveryClient discoveryClient;
    /**
     * 可直接注入Feign的远程调用接口
     */
    @Autowired
    private ProductService productService;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 用户下单接口
     */
    @RequestMapping("/order/prod/{pid}")
    public Order order(@PathVariable("pid") Integer pid) {
        log.info("订单微服务-下单-开始 pid:{}", pid);

        //调用商品微服务查询商品信息
        //1.写法1：写死服务提供者ip端口；1）当服务提供者ip发生变化时代码就会出错；2）当服务提供者部署集群时无法实现复杂均衡；3）当服务越来越多时无法管理服务清单
        //Product product = restTemplate.getForObject("http://localhost:8081/product/" + pid, Product.class);

        //2.写法2：通过nacos注册中心元数据，获取服务提供者ip端口，通过DiscoveryClient接口获取；serviceId就是nacos中注册的服务名
        /*
        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        ServiceInstance serviceInstance = instances.get(0);
        String ip = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = String.format("http://%s:%d/product/", ip, port);
        Product product = restTemplate.getForObject(url + pid, Product.class);
         */

        //3.写法3：自定义负载均衡，随机策略
        /*
        List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
        int index = (int) (Math.random() * instances.size());
        ServiceInstance serviceInstance = instances.get(index);
        String ip = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url = String.format("http://%s:%d/product/", ip, port);
        Product product = restTemplate.getForObject(url + pid, Product.class);
         */

        //4.写法4：使用ribbon组件实现客户端负载均衡，直接将url指定为服务名即可，默认负载均衡策略是轮询
        //缺点：1）服务名全部拼到url中，代码可读性不好 2）调用本地方法和远程方法风格不统一
        //Product product = restTemplate.getForObject("http://service-product/product/" + pid, Product.class);

        //5.写法5：使用feign组件，像调用本地方法一样调用远程方法，只需写一个feign的远程调用接口，然后注入；feign内置ribbon，自带负载均衡功能
        Product product = productService.findByPid(pid);
        log.info("订单微服务-下单-查询到商品信息 pid:{} product:{}", pid, JSON.toJSONString(product));

        if (product == null) {
            throw new IllegalArgumentException("商品不存在");
        }

        //判断是否走容错逻辑，测试时直接停掉商品微服务即可模拟
        if (product.getPid() == -100) {
            Order order = new Order();
            order.setOid(-100);
            order.setPname("下单失败");
            return order;
        }

        Order order = new Order();
        order.setUid(1);
        order.setUsername("测试用户");
        order.setPid(pid);
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());
        order.setNumber(1);
        orderService.createOrder(order);

        log.info("订单微服务-下单-创建订单成功 order:{}", JSON.toJSONString(order));

        //生产下单成功MQ，参数1为topic，参数2为消息体
        rocketMQTemplate.convertAndSend("order-topic", order);
        return order;
    }
}
